<template>
  <!-- 使用 ElementPlus 的卡片组件包裹整个图片裁剪与上传区域 -->
  <el-card class="image-cropper-card">
    <!-- 卡片头部模板，显示标题 -->
    <template #header>
      <div class="card-header">图片裁剪与上传</div>
    </template>
    <!-- 第一行布局，用于放置文件上传组件 -->
    <el-row>
      <el-col :span="24">
        <!-- ElementPlus 的上传组件，用于选择图片 -->
        <el-upload
            class="upload-demo"
            action="#"
            :show-file-list="false"
            :before-upload="beforeUpload"
        >
          <!-- 上传按钮 -->
          <el-button type="primary">选择图片</el-button>
        </el-upload>
      </el-col>
    </el-row>
    <!-- 第二行布局，用于放置画布 -->
    <el-row>
      <el-col :span="24">
        <!-- 画布容器，添加边框样式 -->
        <div class="canvas-container">
          <!-- 画布元素，绑定鼠标事件用于裁剪区域选择 -->
          <canvas ref="canvas" @mousedown="startCropping" @mousemove="updateCropArea" @mouseup="stopCropping"></canvas>
        </div>
      </el-col>
    </el-row>
    <!-- 第三行布局，用于放置裁剪和上传按钮 -->
    <el-row>
      <el-col :span="12">
        <!-- 裁剪按钮，点击触发裁剪操作 -->
        <el-button type="primary" @click="cropImage">裁剪</el-button>
      </el-col>
      <el-col :span="12">
        <!-- 上传按钮，裁剪图片存在时可用，点击触发上传操作 -->
        <el-button type="primary" @click="uploadCroppedImage" :disabled="!croppedImageUrl">上传裁剪后的图片</el-button>
      </el-col>
    </el-row>
    <!-- 第四行布局，当有裁剪后的图片时显示图片 -->
    <el-row v-if="croppedImageUrl">
      <el-col :span="24">
        <!-- 显示裁剪后的图片 -->
        <img :src="croppedImageUrl" alt="Cropped Image" class="cropped-image" />
      </el-col>
    </el-row>
    <!-- 第五行布局，当有上传状态信息时显示提示框 -->
    <el-row v-if="uploadStatus">
      <el-col :span="24">
        <!-- 上传成功时显示成功提示框 -->
        <el-alert
            :title="uploadStatus"
            type="success"
            :closable="false"
            v-if="uploadStatus.includes('成功')"
        ></el-alert>
        <!-- 上传失败或出现错误时显示错误提示框 -->
        <el-alert
            :title="uploadStatus"
            type="error"
            :closable="false"
            v-else
        ></el-alert>
      </el-col>
    </el-row>
  </el-card>
</template>

<script setup>
import { ref, onMounted } from 'vue';

// 引用画布元素
const canvas = ref(null);
// 画布的 2D 上下文
const ctx = ref(null);
// 图片对象
const image = ref(new Image());
// 是否正在进行裁剪操作的标志
const isCropping = ref(false);
// 裁剪区域的起始 x 坐标
const startX = ref(0);
// 裁剪区域的起始 y 坐标
const startY = ref(0);
// 裁剪区域的结束 x 坐标
const endX = ref(0);
// 裁剪区域的结束 y 坐标
const endY = ref(0);
// 裁剪后图片的 URL
const croppedImageUrl = ref('');
// 图片的缩放比例
const scale = ref(1);
// 上传状态信息
const uploadStatus = ref('');

// 组件挂载后执行的钩子函数
onMounted(() => {
  // 获取画布元素
  const canvasElement = canvas.value;
  // 设置画布宽度为父容器的宽度
  canvasElement.width = canvasElement.parentNode.offsetWidth;
  // 设置画布高度为父容器的高度
  canvasElement.height = canvasElement.parentNode.offsetHeight;
});

// 上传前的处理函数
const beforeUpload = (file) => {
  const reader = new FileReader();
  reader.onload = (event) => {
    // 设置图片的源为读取到的文件内容
    image.value.src = event.target.result;
    image.value.onload = () => {
      const canvasElement = canvas.value;
      const imgWidth = image.value.width;
      const imgHeight = image.value.height;
      const canvasWidth = canvasElement.width;
      const canvasHeight = canvasElement.height;

      // 计算图片的缩放比例，使图片按比例适应画布
      const scaleX = canvasWidth / imgWidth;
      const scaleY = canvasHeight / imgHeight;
      scale.value = Math.min(scaleX, scaleY);

      // 获取画布的 2D 上下文
      ctx.value = canvasElement.getContext('2d');
      // 清空画布
      ctx.value.clearRect(0, 0, canvasWidth, canvasHeight);
      // 按比例绘制图片到画布上
      ctx.value.drawImage(
          image.value,
          0,
          0,
          imgWidth,
          imgHeight,
          0,
          0,
          imgWidth * scale.value,
          imgHeight * scale.value
      );
    };
  };
  // 读取文件内容为 DataURL
  reader.readAsDataURL(file);
  // 阻止默认的上传行为
  return false;
};

// 开始裁剪操作
const startCropping = (e) => {
  if (image.value.src) {
    isCropping.value = true;
    // 获取鼠标按下时的 x 坐标
    startX.value = e.offsetX;
    // 获取鼠标按下时的 y 坐标
    startY.value = e.offsetY;
  }
};

// 更新裁剪区域
const updateCropArea = (e) => {
  if (isCropping.value) {
    // 获取鼠标移动时的 x 坐标
    endX.value = e.offsetX;
    // 获取鼠标移动时的 y 坐标
    endY.value = e.offsetY;
    const canvasElement = canvas.value;
    const imgWidth = image.value.width;
    const imgHeight = image.value.height;

    // 清空画布
    ctx.value.clearRect(0, 0, canvasElement.width, canvasElement.height);
    // 重新绘制图片
    ctx.value.drawImage(
        image.value,
        0,
        0,
        imgWidth,
        imgHeight,
        0,
        0,
        imgWidth * scale.value,
        imgHeight * scale.value
    );

    // 设置裁剪区域的边框颜色
    ctx.value.strokeStyle = 'red';
    // 绘制裁剪区域的边框
    ctx.value.strokeRect(
        Math.min(startX.value, endX.value),
        Math.min(startY.value, endY.value),
        Math.abs(endX.value - startX.value),
        Math.abs(endY.value - startY.value)
    );
  }
};

// 停止裁剪操作
const stopCropping = () => {
  isCropping.value = false;
};

// 执行裁剪操作
const cropImage = () => {
  if (image.value.src) {
    const cropCanvas = document.createElement('canvas');
    const cropCtx = cropCanvas.getContext('2d');
    const x = Math.min(startX.value, endX.value) / scale.value;
    const y = Math.min(startY.value, endY.value) / scale.value;
    const width = Math.abs(endX.value - startX.value) / scale.value;
    const height = Math.abs(endY.value - startY.value) / scale.value;

    cropCanvas.width = width;
    cropCanvas.height = height;
    // 从原图片中裁剪出指定区域
    cropCtx.drawImage(image.value, x, y, width, height, 0, 0, width, height);
    // 将裁剪后的图片转换为 DataURL
    croppedImageUrl.value = cropCanvas.toDataURL('image/jpeg');
  }
};

// 上传裁剪后的图片
const uploadCroppedImage = async () => {
  if (croppedImageUrl.value) {
    uploadStatus.value = '正在上传...';
    try {
      // 将 DataURL 转换为 Blob 对象
      const response = await fetch(croppedImageUrl.value);
      const blob = await response.blob();
      const formData = new FormData();
      // 将 Blob 对象添加到表单数据中
      formData.append('image', blob, 'cropped_image.jpg');

      // 发送 POST 请求上传图片
      const uploadResponse = await fetch('/upload', {
        method: 'POST',
        body: formData
      });

      if (uploadResponse.ok) {
        uploadStatus.value = '上传成功！';
      } else {
        uploadStatus.value = '上传失败，请稍后重试。';
      }
    } catch (error) {
      uploadStatus.value = '上传过程中出现错误：' + error.message;
    }
  }
};
</script>

<style scoped>
.image-cropper-card {
  margin-top: 20px;
  background: #fff;
}

.canvas-container {
  margin-top: 20px;
  margin-bottom: 20px;
}

canvas {
  display: block;
  width: 100%;
  height: auto;
}

.cropped-image {
  max-width: 100%;
  height: auto;
  margin-top: 20px;
}
</style>